--- 模块功能：阿里云功能测试.
-- 支持数据传输和OTA功能
-- @author openLuat
-- @module aLiYun.testALiYun
-- @license MIT
-- @copyright openLuat
-- @release 2018.04.14

require"util"
module(...,package.seeall)

require"pins"
require"misc"
require"pm"
require"common"
local lpack = require"pack"

local base = _G

local setGpioFnc_RS485_EN = pins.setup(pio.P0_22,0)

--[[
函数名：
功能  ：
参数  ：
返回值：无
]]
function modbusStructFrame(addr, funcCode, payload)

    local binCentent = lpack.pack(">bbA",addr, funcCode, payload)
    local crc16 = util.crc16modbus(binCentent)
    local Frame = lpack.pack(">AH",binCentent, crc16)
    log.info("Frame:",util.binstohexs(Frame))

    return Frame
end

function tointeger( x )
    num = tonumber( x )
    return num < 0 and math.ceil( num ) or math.floor( num )
end

--[[
函数名：
功能  ：
参数  ：
返回值：无
]]
function modbusFuncCode03(binData)
    local iRegIndex = 0

    usNRegs = string.byte(binData,3)
    usNRegs = usNRegs / 2

    log.info("modbusFuncCode03 usNRegs:",usNRegs)
    iRegIndex = 4

    while( usNRegs > 0 )
    do
       local num = tonumber(util.binstohexs(string.sub(binData, iRegIndex, iRegIndex + 1)), 16)
       log.info("modbusRx iRegIndex:",iRegIndex,tointeger(-4))

       iRegIndex = iRegIndex + 2

       usNRegs = usNRegs - 1
    end
end
--[[
函数名：
功能  ：
参数  ：
返回值：无
]]
function modbusDataParse(binData, len)
    if binData ~= nil then
        log.info("modbusDataParse:",util.binstohexs(binData))
         
        local addr = string.byte(binData,1)
        local funcCode = string.byte(binData,2)
        local frameCRC16 = tonumber(util.binstohexs(string.sub(binData, -2, -1)), 16)

        log.info("modbusRx:",addr, funcCode, len, frameCRC16)

        local mb_crc16 = util.crc16modbus(string.sub(binData, 1, -3))
        log.info("mb_crc16:",mb_crc16)

        if frameCRC16 == mb_crc16 then
            if funcCode == 0x03 then
                if len >= 8 then
                    modbusFuncCode03(binData)
                end
            end
        else
            log.info("modbusRx frameCRC16.error")
        end
    else
        log.info("binData.nil err")
    end
end
--设置新固件下载结果的回调函数
--aLiYunOta.setCb(otaCb)
--[[
功能定义：
uart接收数据，如果100毫秒没有收到新数据，则打印出来所有已收到的数据，清空数据缓冲区，回复received x frame给对端，然后等待下次数据接收
注意：
串口帧没有定义结构，仅靠软件延时，无法保证帧的完整性，如果对帧接收的完整性有严格要求，必须自定义帧结构（参考testUart.lua）
因为在整个GSM模块软件系统中，软件定时器的精确性无法保证，例如本demo配置的是100毫秒，在系统繁忙时，实际延时可能远远超过100毫秒，达到200毫秒、300毫秒、400毫秒等
设置的延时时间越短，误差越大
]]


--串口ID,2对应uart2
--如果要修改为uart1，把UART_ID赋值为2即可
local UART_ID = 1

local function taskUartXRead()
    local cacheData,frameCnt = "",0
    while true do
        local s = uart.read(UART_ID,"*l")
        if s == "" then            
            if not sys.waitUntil("UART_RECEIVE",100) then
                --uart接收数据，如果100毫秒没有收到数据，则打印出来所有已收到的数据，清空数据缓冲区，等待下次数据接收
                --注意：
                --串口帧没有定义结构，仅靠软件延时，无法保证帧的完整性，如果对帧接收的完整性有严格要求，必须自定义帧结构（参考testUart.lua）
                --因为在整个GSM模块软件系统中，软件定时器的精确性无法保证，例如本demo配置的是100毫秒，在系统繁忙时，实际延时可能远远超过100毫秒，达到200毫秒、300毫秒、400毫秒等
                --设置的延时时间越短，误差越大
                if cacheData:len()>0 then
                    log.info("testUartTask.taskRead","100ms no data, received length",cacheData:len())
                    --数据太多，如果全部打印，可能会引起内存不足的问题，所以此处仅打印前1024字节
                    --log.info("testUartTask.taskRead","received data",cacheData:sub(1,1024))
                    if cacheData:len() >= 6 then
                        modbusDataParse(cacheData, cacheData:len())
                    end
                    cacheData = ""
                    --frameCnt = frameCnt+1
                    --uartxWrite("received "..frameCnt.." frame")
                end
            end
        else
            cacheData = cacheData..s            
        end
    end
end

--[[
函数名：uartxWrite
功能  ：通过串口发送数据
参数  ：
        s：要发送的数据
返回值：无
]]
function uartxWrite(s)
    --log.info("testUartTask.write",s)
    setGpioFnc_RS485_EN(1)
    uart.write(UART_ID,s)
end

function writeOk()
    log.info("testUartTask.writeOk")
    setGpioFnc_RS485_EN(0)
end


--保持系统处于唤醒状态，此处只是为了测试需要，所以此模块没有地方调用pm.sleep("testUartTask")休眠，不会进入低功耗休眠状态
--在开发“要求功耗低”的项目时，一定要想办法保证pm.wake("testUartTask")后，在不需要串口时调用pm.sleep("testUartTask")
pm.wake("testUartTask")
--注册串口的数据发送通知函数
uart.on(UART_ID,"sent",writeOk)
uart.on(UART_ID,"receive",function() sys.publish("UART_RECEIVE") end)
--配置并且打开串口
uart.setup(UART_ID,9600,8,uart.PAR_NONE,uart.STOP_1, nil, 1)
--uart.set_rs485_oe(UART_ID, pio.P0_22, 1)
--如果需要打开“串口发送数据完成后，通过异步消息通知”的功能，则使用下面的这行setup，注释掉上面的一行setup
--uart.setup(UART_ID,115200,8,uart.PAR_NONE,uart.STOP_1,nil,1)
--启动串口数据接收任务
sys.taskInit(taskUartXRead)

--[[
函数名：
功能  ：
参数  ：
返回值：无
]]
function App_read_sensors()

    local payload = lpack.pack(">HH",0x0000, 0x0002)

    uartxWrite(modbusStructFrame(0x01, 0x03, payload))

end

--每隔1秒输出1次当前模块系统时间
sys.timerLoopStart(App_read_sensors,5000)

